home *** CD-ROM | disk | FTP | other *** search
Text File | 1986-07-01 | 43.9 KB | 1,162 lines |
- dBASE III Tips (DB3N8509.TXT)
-
- 1. dBASE III Anomalies and Workarounds
-
- >>> & Function
-
- The macro (&) function will not expand properly if it is followed
- by a space and parentheses. For example:
-
- STORE 'LIST FOR' TO x
- STORE 10 TO memvar
- &x Field1 = memvar
-
- will execute properly, but,
-
- &x (Field1 - memvar) * memvar = 0
-
- will return the "*** Unrecognized command verb" error message. This
- problem can be avoided by terminating the macro-substituted memory
- variable with a period. For example,
-
- &x. (Field1 - memvar) * memvar = 0
-
- will work. It is always a good idea to terminate a macro with a period.
-
-
- >>> CONFIG.DB with TEDIT or WP
-
- Config.db will not accept more than eight characters for WP or
- TEDIT. Any more than eight will be truncated. Attempting to use
- MODIFY COMMAND (or to edit a MEMO field) will briefly display the
- operating system message "Bad command or file name" and drop the user
- to the dBASE dot prompt (or to the edit screen). dBASE III warns the
- user that the filename is truncated when dBASE is initialized.
- For example:
-
- TEDIT=B:DFORMAT
- ^--- truncated
-
- To work around this problem, place the word processor in the same drive
- and directory or rename the wordprocessor with fewer characters.
-
-
- >>> DO WHILE with RESTORE
-
- If a memory variable tested in a DO WHILE loop is recreated in the
- loop by RESTOREing the variable FROM a memory file, the loop will
- continue to run even after the condition no longer evaluates as true
- (.T.). The program below will run endlessly as long as the control
- variable is not the first entry in the memory file:
-
-
-
-
-
- var = .T.
- DO WHILE var
- RESTORE FROM Memfile <--- This overwrites var at the same
- memory location.
- var = .F. <--- This change is ignored if the
- ENDDO previous assignment statement
- changed the memory location of the
- variable.
-
- RESTOREing ADDITIVE ameliorates the problem.
-
-
- >>> DO WHILE with semicolon
-
- When a DO WHILE conditional statement is continued to a second
- line with a semicolon, dBASE III tries to execute this second line the
- second time through the loop. When the ENDDO is encountered and the
- condition evaluates as true, program flow proceeds to this second line,
- resulting in the error message, "*** Unrecognized command verb."
-
- * ---This program will give an error message
- * ---when "Y" is entered at the WAIT prompt.
- answer = 'Y'
- number = 1
- DO WHILE number < 10;
- .AND. answer = 'Y'
- ? number
- WAIT '"Y" to continue ' TO answer
- number = number + 1
- ENDDO
-
- If "Y" is entered at the WAIT prompt, dBASE III tries to execute ".AND.
- answer = 'Y'." However, if the semicolon is deleted and the line is
- allowed to wrap at column 67 in MODIFY COMMAND, execution flows
- correctly.
-
-
- >>> DO WHILE with RETURN
-
- A RETURN statement inside a DO WHILE...ENDDO construction will not
- close the DO WHILE <condition> in the program as it does in dBASE II.
- Therefore, the <condition> will continue to be evaluated. For example:
-
- * A.PRG
- expA = "1"
- DO WHILE expA = "1"
- ? "Hi!"
- DO B ---------------> * B.PRG
- ENDDO expB = "2"
- * EOF: A.PRG DO WHILE expB = "2"
- ? "How are you?"
- expA = "X"
- RETURN
- ENDDO
- * EOF: B.PRG
-
- Executing A.PRG will cause an infinite loop. It appears dBASE III
- continues to test the condition of expB. In order to work around this,
- B.PRG should be written as follows:
-
- * B.PRG (revised).
- expB = "2"
- DO WHILE expB = "2"
- ? "How are you?"
- expA = "X"
- EXIT <----- Notice, the EXIT here,
- ENDDO
- RETURN <----- and the RETURN here.
- * EOF: B.PRG
-
-
- >>> DO WHILE with an extra ENDDO
-
- If an extra ENDDO is added to a command file, an infinite loop
- will result. For example, the following program will execute until Esc
- is pressed:
-
- number = 0
- DO WHILE number < 10
- @ 10,10 SAY "Now at loop " + STR(number,2)
- STORE number + 1 TO number
- ENDDO
- ENDDO <--- This ENDDO has no matching DO WHILE.
- RETURN
-
- We recommend you use some method of indentation for the control
- structures: DO CASE...ENDCASE, DO WHILE...ENDDO, and IF...ENDIF to
- avoid this problem. This practice will make command files more
- readable, and will allow for quick visual checking for accuracy of
- nested control structures. In our example, two consecutive ENDDO
- statements with the same left margin is a definite indication that
- something is wrong.
-
-
- >>> Memo fields, listing or printing after a previous field
-
- A problem displaying memo fields has been found in dBASE III by
- one of our users. The problem occurs when you try to list or print a
- memo field preceded by a field whose length will force the first line
- of the memo field to wrap around one or more times. Assume a file
- structure consisting of two fields:
-
- Structure for database: Example.DBF
- Field Field Name Type Width Dec
- 1 Field1 Character 100
- 2 Field2 Character 70
- 3 Comments Memo 10
- ** Total ** 181
-
-
- These display commands
-
- ? SPACE( 1 ), Comments
- ? Field1, Comments
- ? Field2, Comments
- ? Field1, Field2, Comments
-
- will all produce different results.
-
- If the total length of the fields preceding the memo field is 29
- or above, the first line of the memo field will wrap around. The
- entire memo field will then be displayed in double space. Furthermore,
- if the total length of the fields preceding the memo field is long
- enough to force the first line of the memo field to be displayed
- starting on the second line of the display, then the entire memo field
- will be displayed in triple space.
-
-
- >>> REPORT FORM with right margin=report width
-
- CREATEing a REPORT FORM with a right margin value equal to the
- report width (the page width minus the left margin) will display
- garbage to the screen or printer. This happens because there is no
- space to print the report.
-
- There is a general misconception about the meaning of the right
- margin in the REPORT FORM. Some users have the impression that its
- value is the number of characters from the left margin, much the same
- way a typewriter works. The value actually refers the the number of
- characters the right margin is offset from page width. For example,
-
- page width |------------------------------------->|
- right margin |<----------|
-
-
- >>> REPORT FORM with stacked columns
-
- CREATEing a REPORT FORM field that consists of stacked columns
- built with the result of the STR() function and numeric fields may
- cause numbers to display incorrectly. Specifically, entering:
-
- STR( Field1, 5 ) + STR( Field2, 5 )
-
- in the field contents and specifying a column width of five will result
- in a correct display only when the fields have five-digit numbers
- stored in them. If the number has less than five digits, the display
- will be misplaced by the number of digits missing. The example above
- will produce:
-
- 11111
- 11111
-
-
-
-
- if the fields are full, but:
-
- 1111
- 1111
- 1111
-
- if there are only four digits in the fields.
-
-
- >>>SET FILTER TO with GO BOTTOM
-
- If a SET FILTER TO condition is not satisfied by any records in
- the database file and a GO BOTTOM is issued, both the EOF() and BOF()
- will return a true (.T.). Removing the filter by issuing a SET FILTER
- TO does not reset EOF() or BOF(). The record pointer must be
- repositioned to reset EOF() and BOF(). SKIP or SKIP -1, however, will
- return a file boundary error message, because EOF() and BOF() are true.
- To move the record pointer appropriately issue a GO TOP and the BOF()
- and EOF() values will be reset to false (.F.).
-
-
- >>> Get Current Directory
-
- dBASE III has no facility to get the name of the current directory.
- To get it you must RUN the PC/MS-DOS command CD and import the results
- into dBASE III. The basic algorithm is as follows:
-
- 1. Create or have available a general-purpose database file called
- Util.DBF. Util.DBF has one field called Util_line which is character
- type and has a length of 80.
- 2. RUN the PC/MS-DOS command CD, piping the result into a text
- file entitled Util.TXT.
- 3. APPEND the text file Util.TXT into the database file, Util.DBF.
- 4. Assign to a memory variable the name of the current DOS
- directory from Util.DBF.
-
- The code that will execute this algorithm is as follows:
-
- SET SAFETY OFF
- RUN CD > Util.TXT
- USE Util
- ZAP
- APPEND FROM Util SDF
- currdir = TRIM( Util_line )
- SET SAFETY ON
- RETURN
-
-
- >>> Get Diskspace
-
- In the Developer's Release use the DISKSPACE() function to get the
- amount of space left on the currently logged drive. The DISKSPACE()
- will return the number of free bytes on the default drive as a numeric
- value.
-
- dBASE III versions 1.0 and 1.1 do not have a function to return
- the amount of space left on the default drive. So, to get the amount
- of diskspace in these versions, use the PC/MS-DOS utility CHKDSK and
- import the results into dBASE III. The basic algorithm is as follows:
-
- 1. Create or have available a general purpose database file called
- Util.DBF. Util.DBF has one field called Util_line which is character
- type and has a length of 80. This database file will be useful for any
- of these kinds of survey operations into PC/MS-DOS.
-
- 2. RUN CHKDSK including the designator of the drive for which you
- want the space statistic for, and pipe the result into a text file
- entitled Util.TXT. Piping is a PC/MS-DOS capability that allows the
- results of a program to be sent into a text file. It is very useful
- for passing parameters between programs when there is no formalized
- interface. The syntax is:
-
- <DOS commmand> > <result text file>
-
- ^_____ DOS piping symbol
-
- For more information on this capability, consult your PC/MS-DOS
- reference manual.
-
- 3. APPEND the text file, Util.TXT, into the database file,
- Util.DBF.
-
- 4. Assign to a memory variable the number of free bytes on the
- specified drive from Util.DBF. This operation requires that you GOTO
- the record that contains the free disk space information and then
- extract the number of bytes from the field using the SUBSTR() function.
-
- The following is a LIST of Util.DBF with the results of a CHKDSK
- report. When APPENDed into a database file, the first and last records
- are always blank. Records 2 through 6 contain statistics about the
- currently logged disk drive. Note that this is the currently logged
- DOS drive and not the DEFAULT drive SET in dBASE III. Records 8 and 9
- contain statistics about the memory configuration of your computer.
- The number of bytes for each attribute of the drive and memory occupy
- positions 1 through 9 in the database field, Util_line.
-
- Record#
- 1
- 2 9965568 bytes total disk space
- 3 155648 bytes in 4 hidden files
- 4 90112 bytes in 22 directories
- 5 6000640 bytes in 397 user files
- 6 3719168 bytes available on disk
- 7
- 8 524288 bytes total memory
- 9 122480 bytes free
- 10
-
- The code that will get the the number of free bytes on the
- specified disk drive is as follows:
-
- SET SAFETY OFF
- RUN CHKDSK > Util.TXT
- USE Util
- ZAP
- APPEND FROM Util SDF
- GO 6
- diskspace = STR( SUBSTR( Util_line, 1, 9 ), 9 )
- USE
- SET SAFETY ON
- RETURN
-
-
- >>> Get Last Update and Time
-
- To get the date of last update for the currently SELECTed database
- file in the Developer's Release of dBASE III, use the LUPDATE()
- function. LUPDATE() returns the date of last update as a value of date
- type.
-
- dBASE III versions 1.0 and 1.1 currently do not have a function
- that returns the date of last update for the SELECTed database file.
- To get the date of last update in these versions of dBASE III, use the
- PC/MS-DOS command DIR, and import the results into dBASE III. The
- basic algorithm is as follows:
-
- 1. Create or have available a general purpose database file
- called Util.DBF. Util.DBF has one field called Util_line which is
- character type and has a length of 80.
-
- 2. RUN the PC/MS-DOS command DIR with the name of your database
- file, piping the result into a text file entitled Util.TXT.
-
- 3. APPEND the text file Util.TXT into the database file Util.DBF.
-
- 4. Assign to a memory variable the date of last update from
- Util.DBF for your database file.
-
- The code that will get the last update of the currently SELECTed
- database file is as follows:
-
- SET SAFETY OFF
- RUN DIR <Yourfile>.DBF > Util.TXT
- USE Util
- ZAP
- APPEND FROM Util SDF
- lupdate = SUBSTR( Util_line, 25, 8 )
- luptime = SUBSTR( Util_line, 34, 6 )
- USE
- SET SAFETY ON
- RETURN
-
-
-
-
- >>>Installation
-
- If the message "Insert System Disk #2 or press Ctrl-Break" appears
- when dBASE III is being loaded from a hard disk and is installed, the
- overlay file (DBASE.OVL) is corrupted. One possibility is that the
- copy on the hard disk is corrupted and can no longer be used. Another,
- and more likely, possibility is that the copy on System Disk #2 is bad,
- and you are now trying to run dBASE III after just having installed to
- the hard disk. If this occurs, contact the Ashton-Tate Customer
- Service Department.
-
-
- >>> MEMO fields
-
- (1) MEMO fields are used to contain up to 5,000 characters of text
- information that is to be associated with a database record.
- Information may be read into a MEMO field using Ctrl-K-R and written to
- text files using Ctrl-K-W. Information from MEMO fields can be
- displayed or printed by using LIST, DISPLAY, ?. The field must be
- specified with these commands. However, these commands cause the MEMO
- field to wrap at 50 columns. The REPORT FORM may be used to output
- MEMO fields with line widths of more or less than 50 characters.
-
- (2) PACKing a database file with memo fields will not decrease the
- amount of disk space used by the .DBT file. The command file below
- demonstrates how to remove the deleted records and free the unused disk
- space.
-
- SET DELETED ON
- USE Filea
- COPY TO Temp
- CLOSE DATABASE
- ERASE Filea.dbf
- ERASE Filea.dbt
- RENAME Temp.dbf TO Filea.dbf
- RENAME Temp.dbt TO Filea.dbt
- SET DELETED OFF
-
-
- >>> Ramdisk
-
- There are several options for users who wish to use a ramdisk in
- combination with dBASE III.
-
- 1. For faster operation of applications that utilize routines
- stored in the DBASE.OVL file, you may wish to put the .OVL in a ramdisk.
-
- (a) The minimum drive size will have to be in excess of
- 181,000 bytes. The DBASE.OVL file for version 1.1 is 180,736 bytes.
- The total amount of RAM in your machine must be more than 440,000 bytes
- in order to do this. Additionally, if you are using a CONFIG.DB, it
- must be present on the drive where .OVL resides.
- (b) Boot dBASE III from the ramdisk by calling for the
- DBASE.COM from the drive on which it resides. For example: drive D:
- is the ramdisk and the DBASE.COM is on the C: drive.
-
- C> D:
- D> C:DBASE
-
- 2. It may be a very useful area for procedure or command files to
- be run from, increasing the speed of processing. Prior to entering
- dBASE III, copy the appropriate files to the ramdisk. Once in dBASE
- III, SET the DEFAULT TO the ramdisk drive and proceed.
-
- 3. It is also useful as a small work area to manipulate utility
- and temporary files. The useage tips on getting the current directory
- or diskspace are good examples of where a small ramdisk would be
- extremely useful and time efficient.
-
-
- >>> REPORT FORM
-
- The semicolon is not documented as functioning as a Carriage
- Return/Line-Feed in certain parts of REPORT FORMs.
-
-
- >>> REPORT FORM grouped by week
-
- If you have a date-oriented report and you need to have it grouped
- by week, the following discussion will assist you.
-
- The grouping of dates into weeks has two requirements. First, the
- database file you are reporting from must be INDEXed on the date field
- that is being grouped on. Second, as the group expression in your
- REPORT FORM, you must have an expression that returns as its value the
- first day of the week for each date field. The expression is as
- follows:
-
- Yourdate - ( DOW( Yourdate ) - 1 )
-
- When given any date value, this expression returns the date of the
- previous Sunday. It does this by subtracting from your date field the
- number of days that have passed since the last Sunday, the first day of
- the week in the dBASE III calendar. This value is obtained by
- subtracting 1 from the result of the DOW() function.
-
- If you wish to have the week you are grouping on start on a later
- day such as Monday, subtract more from the result of the DOW() function.
- For example, Monday would be DOW() - 2, Tuesday DOW() - 3, and so on.
-
-
- >>>WAIT TO
-
- When a function key is SET to a literal character string, the WAIT
- command will not accept the assigned string, although ACCEPT TO will.
- Instead, the WAIT command will take the ASCII code representation of
- the function key itself. For example:
-
- SET FUNCTION 10 TO "A"
- WAIT TO var
-
- * ---Press F10.
- DISPLAY MEMORY
-
- VAR pub C "v"
- 1 variables defined, 3 bytes used
- 255 variables available, 5997 bytes available
-
- * ---Test to see what ASCII character F10 sent.
- ? ASC(var)
- 246 <------------------------- Code for F10
- ? ASC("A")
- 65 <-------------------------- Code for "A"
-
-
- >>> Shifted Data Displays by Oliver Biggerstaff
-
- A database file may become corrupted for any number of reasons.
- Often the corruption may be the form of shifted data in full-screen
- edit screen displays. This is caused by an embedded null character in
- a record. A null character is represented as a 00 hex and is used by
- dBASE II and dBASE III as a string terminator. A string terminator is
- a character that can be thought of as a delimiter, much like double
- quotes surrounding a character string, or as a carriage return and
- linefeed at the end of a record.
-
- The dBASE APPEND, EDIT, BROWSE, and other full-screen commands
- work on the principle that the cursor's positioning on the screen
- depends on certain attributes, such as the length of a field and its
- current position. The appearance of shifted data is caused by the
- embedded string terminator forcing dBASE to stop the display of a field
- prematurely, placing the cursor at an incorrect location. If a null
- character is encountered before all the characters of a field have been
- displayed, dBASE will stop listing that particular field and will
- produce the shift effect by displaying the next field at an incorrect
- screen location.
-
- The data is actually not shifted physically in the database file.
- It is simple, therefore, to correct the database by replacing any null
- character with another character that does not force dBASE to display
- incorrectly. A good choice for this character is the ASCII character
- zero (0) or 30 hex. Replacing a null with this character is
- advantageous for two reasons:
-
- 1. Since dBASE can display it, you can locate the corrupted data.
-
- 2. A zero character will have the least disruptive effect on the
- contents of the database file. Additionally, logical fields that
- contain a zero character will be displayed as (.F.)
-
- Once all the null characters have been replaced, the BROWSE or
- EDIT commands can be used to retype the original data in place of the
- characters that replaced the nulls.
-
-
- The following is one of many methods that can be used to replace
- null characters with other characters. In this example we use the
- PC/MS-DOS utility DEBUG.COM, since most of you have this program on
- your supplemental PC/MS-DOS disk. These examples assume that the
- database file is 64K or less in size. Refer to the PC/MS-DOS manual
- for more information on DEBUG and how to use data segments if the
- database file is larger than 64K.
-
- The contents contained in the < and > symbols must be calculated
- by you, and entered without the symbols. For example, if the value of
- the CX register is 2C80H, then <CX+100H> is to be replaced with 2D80H.
- Be sure that before you attempt this procedure, you have make a backup
- of your database file.
-
- For dBASE II users on a 16-bit computer:
-
- C>DEBUG <database>.DBF ;Read database file into memory.
- -RCX ;Get the value in the CX register
- -S 309 <CX+100H> 00 ;Search for nulls in the file.
- .
- . ;A list appears here of one or more
- . ;addresses containing a null.
- .
- -E <address> 30 ;Replace each individual address that contains
- ;a null with a zero.
- -W ;Save the modifications to disk.
- -Q ;Quit DEBUG.
-
- For dBASE III users:
-
- C>DEBUG <database>.DBF ;Read database file into memory.
- -RCX ;Get the value of the CX register.
- -S 100H 1121H 0DH ;Search for the end of the header for address
- ;containing 0DH.
- xxxx:yyyy ;for address containing 0D.
- ;Search for null characters.
-
- -S <yyyy+2> <CX+100H> 00
- .
- . ;List of addresses containing
- . ;nulls.
- .
- -E <address> 30H ;Replace each null with a zero.
- -W ;Save the modifications to disk.
- -Q ;Quit DEBUG.
-
- Unfortunately, this method of replacing null characters can be
- very tedious if many characters must be replaced. For those of you
- with a large amount of corrupted data, it is suggested that you use
- other well-known utility programs such as NIBBLER, NORTON UTILITIES,
- JAZ, or PATCH. These programs will allow you to look at very large
- database files directly from the hard disk. Some of these programs
- may also have a global search and replace option.
-
-
- >>> Swapping Printer Ports on the IBM PC by Robert Boies
-
- Last month in the dBASE II section of TechNotes we presented an
- assembler routine to swap printer ports on an IBM PC. This routine
- allows the user or programmer to toggle the LPT1 and LPT2 ports, making
- it easy to redirect output to several printers. This month we present
- essentially the same routine configured for dBASE III.
-
- If you have versions 1.0 or 1.1 of dBASE III, you will have to
- create a .COM file from this routine and at runtime use the RUN command
- to call it. For example from within dBASE III,
-
- * ---Print a first report to the default port, LPT1.
- REPORT FORM One TO PRINT
-
- * ---Print a second report to LPT2.
- RUN Portswap <---------------------------- Redirect to LPT2.
- REPORT FORM Two TO PRINT
- RUN Portswap <---------------------------- Restore LPT1.
-
- If you have the Developer's Release of dBASE III, you will be able
- to use the assembly language interface implemented in that version.
- For example:
-
- * ---LOAD into memory.
- LOAD Portswap
-
- * ---Print a first report to to the default port, LPT1.
- REPORT FORM One TO PRINT
-
- * ---Print a second report to LPT2.
- CALL Portswap <---------------------------- Redirect to LPT2.
- REPORT FORM Two TO PRINT
- CALL Portswap <---------------------------- Restore LPT1.
-
- * ---RELEASE the memory space.
- RELEASE MODULE Portswap
-
- The listing below, Portswap.ASM, uses the conditional directives
- discussed earlier to allow you to specify whether you wish to create
- a .BIN or .COM file.
-
- Assembly language source code:
-
- ; Program ..: Portswap.ASM
- ; Author ...: Robert Boies
- ; Date .....: September 1, 1985
- ; Note .....: Swaps the LPT1 and LPT2 ports in PC/MS-DOS 2.x
-
- .LFCOND ; List false conditionals.
- PAGE 60,132 ; Page length 60, line 132.
-
- COM EQU 0 ; Assemble as .BIN file
- D3 EQU 1 ; for Developer's Release.
- ;
- CODESEG SEGMENT BYTE PUBLIC 'CODE'
- ASSUME CS:CODESEG
- ;
- PORTSWAP PROC FAR
- ;
- IF COM
- ORG 0100H ; Originate at 0100H.
- ENDIF
- ;
- START: PUSH ES ; Save environment.
- PUSH AX
- PUSH BX
- PUSH DI
- ;
- MOV AX,40H ; System stores critical operating
- ; parameters at segment 40H (absolute
- ; address 400H).
- ; Load this segment address into AX.
- PUSH AX ; Put it on the stack.
- POP ES ; Pop the segment id from the
- ; stack into extra segment register.
- MOV DI,8H ; Port address of LPT1 and LPT2 are
- ; stored at offsets 8H and 0AH in
- ; this segment. Load DI with offset.
- MOV AX,ES:[DI] ; Put the port address of the
- ; first printer into the accumulator.
- INC DI ; Increment the destination index
- ; twice to point to port address
- INC DI ; of the second printer.
- MOV BX,ES:[DI] ; Put the port address of the
- ; second printer into BX.
- MOV ES:[DI],AX ; Poke the address of the first
- ; printer into the location of the
- ; second printer.
- DEC DI ; Decrement DI twice to point to the
- DEC DI ; location of the first printer
- ; port address.
- MOV ES:[DI],BX ; Poke the address of the second
- ; printer into the location of the
- ; first.
- POP DI ; Restore environment.
- POP BX
- POP AX
- POP ES
-
- IF COM
- INT 20H ; INT 20H if .COM file
- ELSE
- RET ; far return to dBASE III
- ENDIF
- ;
- PORTSWAP ENDP
- CODESEG ENDS
- END START
-
- >>> Interfacing Assembly Language Routines with dBASE by Ralph Davis
-
- Creating Assembler Programs with DEBUG
-
- DEBUG is the assembly language programmer's best friend. It is a
- powerful tool for exploring the computer's memory, testing assembly
- language programs, studying program listings and creating new programs.
- Additionally, it can be used to rebuild corrupted data files, convert
- hidden files to accessible files, or simply analyze file structures.
- Our main interest in DEBUG here is to create assembly language routines
- for use with dBASE II and dBASE III.
-
- It is tempting to use DEBUG because of its interpreter-like
- qualities. You can quickly enter code and then see if it works. If it
- does, you call it <PROGRAM>.COM and write it to disk. If it doesn't,
- you trace through the old code, enter new code, and try again.
- Eventually, you come up with a program that works through trial-and-
- rror. However, this can lead to sloppy programming habits and
- inefficient code, so it is important to bear in mind what you want a
- particular program to accomplish.
-
- DEBUG has some limitations. Most importantly, it only recognizes
- absolute addresses. When you write a program for submission to an
- assembler, you label the instructions and data you will need to refer
- to, then refer to them with the label. You don't need to know the
- actual addresses. DEBUG, on the other hand, obliges you to look
- through your program listing and find addresses whenever you refer to
- them. For instance, instead of entering JMP EXIT, you must enter JMP
- 02FC. Instead of CALL HEXPRINT, you use CALL 05AE. Instead of MOV BX,
- OFFSET DATA, you need MOV BX, 0105. If your routine is small, this
- does not present a problem. But as you add features and it becomes
- larger, this becomes a serious impediment. If you add or alter
- instructions, thereby changing an absolute address, you have to change
- every reference to it. And the only way to find the references is to
- page through the entire program, line by line. For this reason, DEBUG
- is best for creating short utility programs.
-
- Most often, programs created with DEBUG use BIOS or DOS interrupts
- to manipulate the hardware. Some typical functions that appear in this
- issue are setting the cursor (see the example on page 4-72C of the
- Developer's Release Reference Manual and the program listed in this
- issue), manipulating the shift keys, or swapping printer ports.
- Programs of this type should not contain any subroutines.
-
- DEBUG has another important limitation: it only understands
- hexadecimal numbers. There is simply nothing you can do to make it
- accept decimal numbers. This is not a problem when entering addresses
- or interrupt numbers, as most assembly language programmers think these
- values in hexadecimal anyway. But very few programmers think in hex
- when doing calculations. DEBUG is therefore not a good tool for doing
- number-crunching of even intermediate complexity. Although there are
- utilities available to assist in this process, such as Sidekick, this
- is still a major obstacle to doing extensive calculations within DEBUG.
-
-
- Another problem with DEBUG is that code produced with it can be
- extremely obscure. Trying to decipher the flow of a program where you
- have only absolute addresses and hexadecimal numbers to guide you can
- be very frustrating. In addition, DEBUG does not support comments. So
- when you read a DEBUG listing, you are, for all intents and purposes,
- reading "machine English." The machine expresses its own language in
- cryptic English-like symbols, making a few grudging concessions to your
- desire to understand it. All of this reinforces what we suggested
- earlier: keep DEBUG routines short.
-
- The program from the Developer's Release Reference Manual
- mentioned above is a good example of a program appropriate for DEBUG.
- The listing on page 4-72C is as follows:
-
- _PROG SEGMENT BYTE
- ASSUME CS:_PROG
- ;
- CURSOR PROC FAR ; Force a far return.
- ;
- MOV CX,[BX] ; Get two HEX digits.
- MOV AH,1 ; Set cursor type.
- INT 10H ; Video interrupt.
- RET ; Do a far return.
- ;
- CURSOR ENDP
- ;
- _PROG ENDS
- END
-
- This is a terse routine that converts the dBASE III cursor to a
- full-sized box when CHR(18) is passed as a parameter to it. Notice one
- thing about this code: it has six lines of assembler directives (the
- first three and the last three), and only four lines of machine
- instructions. In a short program like this one, there is no advantage
- to assembling, linking, and converting it using MASM, LINK and EXE2BIN.
- DEBUG is faster and easier.
-
- Here is a DEBUG session that enters this program as a .COM file.
- (The DEBUG commands are explained in Chapter 8 of the PC/MS-DOS manual.
- Page numbers which follow refer to it.)
-
- D>debug
-
- First give DEBUG the 'A' (assemble) command (page 8-15) and enter
- the program.
-
- -A
- 6257:0100 MOV CX,[BX]
- 6257:0102 MOV AH,1
- 6257:0104 INT 10
- 6257:0106 INT 20
- 6257:0108
-
-
-
- Notice that 'INT 20' is our last instruction, not 'RET' as the
- manual indicates. We will explain this shortly.
- The address following the last instruction is 108. Therefore,
- enter eight into CX using the 'R' (register) command [page 8-41]. This
- tells DEBUG the number of bytes to write to disk.
-
- -RCX
- CX 0000
- :8
-
- Name the program CURSOR.COM using the 'N' command [page 8-37], and
- write it to disk using 'W' [page 8-55].
-
- -NCURSOR.COM
- -W
- Writing 0008 bytes
-
- This is the basic procedure for creating a .COM file from DEBUG.
- CURSOR.COM will yield unpredictable results executed from PC/MS-DOS,
- since the registers are not preserved, and we have no way of knowing
- what is being passed in DS:BX. (When we tested it, the cursor simply
- vanished.) Nor, in its present form, will it work in dBASE III. It
- needs a couple of changes to make it work, but this point deserves some
- attention.
-
- PC/MS-DOS .COM files and dBASE LOAD modules require slightly
- different specifications. A .COM file must be ORGed (originated) at
- address 100H, and it must end with a command like INT 20H (terminate)
- or INT 27H (terminate and stay resident); a simple RET will not return
- correctly. dBASE III, on the other hand, requires LOAD modules to be
- ORGed at address 0 and to return to dBASE III with a far return, RETF.
- If you load a conventional .COM file, ORGed at 100H and terminated with
- INT 20H, into dBASE III, and then call it, you will lock the system,
- even if it works from PC/MS-DOS. When DEBUG writes a program to disk,
- it writes a binary file -- that is, a file which contains nothing but
- the machine instructions you have given it. Therefore, we need not
- concern ourselves with ORGing programs correctly at this stage. We do
- have to terminate LOAD modules with RETF, however. Here is a DEBUG
- session that enters this program as a .BIN file which will execute from
- dBASE III.
-
- D>debug
-
- Type 'A' for assemble. Terminate with a RETF.
-
- -A
- 6346:0100 MOV CX,[BX]
- 6346:0102 MOV AH,1
- 6346:0104 INT 10
- 6346:0106 RETF
- 6346:0107
-
- Place the number 7 in the CX register to save 7 bytes to disk.
-
-
- -RCX
- CX 0000
- :7
-
- Name the file, and write it.
-
- -NCURSOR.BIN
- -W
- Writing 0007 bytes
-
- Quit DEBUG.
-
- -Q
-
- The page of the Developer's Release Manual referred to above gives
- the following example of how to use Cursor:
-
- LOAD Cursor
- STORE CHR(18) TO shape
- CALL Cursor WITH shape
-
- The commands to convert the cursor back to its normal format are:
-
- LOAD Cursor
- STORE CHR(12) + CHR(11) to shape
- CALL Cursor WITH shape
-
-
- >>> On .COM Files vs. .EXE Files
-
- When creating programs with a full-featured assembler, we have two
- options: .COM files and .EXE files. Each has advantages and
- disadvantages.
-
- .COM files are an inheritance from the world of 8-bit CP/M. They
- are your only option if you have a CP/M machine. .COM files must
- adhere to a strictly defined structure.
-
- 1. They must fit entirely within one segment. All segment
- registers must point to the same address, and cannot be changed during
- the execution of the program. This means that all of our main program,
- subroutines, and data must fit in 64K. A 64K .COM file is a very large
- program -- each line of code assembles to between 1 and 6 bytes, so a
- 64K .COM file could have as many as 30,000 lines of source code.
-
- 2. They must be ORGed at 100H. When PC/MS-DOS loads a .COM file,
- it jumps to CS:100H and begins executing.
-
- 3. They must return control to their calling routine with either
- INT 20H or INT 27H, or the equivalent INT 21H function calls, 4CH and
- 31H.
-
- .COM files load more quickly than .EXE files, since no addresses
- need to be calculated at load time.
-
- The assembly language programs that dBASE II and dBASE III can
- execute as subroutines (with the CALL command) are variations of a .COM
- file. We will discuss the specifics of their formats later.
-
- .EXE files are less limited structurally. The segment registers
- can be freely manipulated, and each one can point to an entirely
- different 64K segment. .EXE files can therefore be much larger than
- .COM files. .EXE files were designed to take better advantage of the
- actual architecture of 16-bit 8086-based microprocessors. Having data
- in one segment, code in another, and the stack in a third allows much
- greater utilization of the memory space available in today's machines.
- It also provides us the semblance of structured programming in assembly
- language. The SEGMENT, PROC, ENDS, and ENDP operators give a program
- listing a much more organized appearance than it has with JMP and DB
- statements interspersed throughout the code.
-
- .EXE files take longer to load than .COM files, as many of the
- absolute addresses are not computed until load time. They also take up
- more disk space than .COM files. However, since they use much more of
- the 8086 family's capabilities, they can be much more powerful programs.
- The commercial programs which were handed down from the CP/M world are
- all .COM files, whereas those which were created since the advent of
- 16-bit machines are mostly .EXE files.
-
- Having said this, we will leave .EXE files behind. You cannot
- LOAD .EXE files from dBASE II or dBASE III. You can execute them with
- QUIT TO in dBASE II or RUN(!) in dBASE III. If you want to pass
- parameters to and from .EXE files, you must pass them in text files
- (the SDF format is recommended).
-
-
- >>> Adapting Assembly Language Programs to dBASE II or III
-
- As mentioned earlier, the format of a dBASE II or III assembly
- language subroutine most closely resembles that of a .COM file. Most
- importantly, it must reside in one segment. Since it is intended as a
- subroutine, not as a stand-alone program, it will differ somewhat from
- a standard .COM file.
-
- For one thing, a .COM file must be ORGed at 100H. However, ORGing
- a dBASE (II or III) subroutine at 100H will cause it to fail. A
- program intended for use in dBASE II must be ORGed high in the code
- segment -- the exact address depends on the version of dBASE II, the
- later the version, the higher the address. In version 2.43*, the ORG
- address should be above 61440 decimal. (See Robert Boies' article on
- swapping printer ports in the August issue of TechNotes for a good
- example of a dBASE II assembly language program.) A program intended
- for dBASE III must be ORGed at 0 (that is, it need not have an ORG
- statement). Secondly, .COM files return to their caller with interrupts
- (usually 20H or 27H), whereas dBASE II and dBASE III routines require
- RET (return) -- near for dBASE II, far for dBASE III.
-
- The procedure for converting assembly language source code into
- programs dBASE II or III can execute are as follows:
-
- 1. For dBASE II, you must assemble your program with an assembler
- that produces a file in Intel .HEX format. Intel's assemblers, ASM
- (for CP/M) and ASM86 (for CP/M-86), create such a file. For PC/MS-DOS,
- the Seattle Computer Products assembler generates a .HEX file. Refer
- to their manuals, as their assembly language syntax differs somewhat
- from Microsoft's and IBM's.
-
- 2. For dBASE III, use the IBM or Microsoft Macro-Assembler
- (MASM.EXE) to produce a .OBJ (object) file. Enter the command as
- follows:
-
- MASM <filename> <filename> <filename>;
-
- The third parameter will cause MASM to produce a listing file with
- a .LST extension, which is very useful for debugging.
-
- 3. Use the linker utility (LINK.EXE) that comes both with
- PC/MS-DOS and with the assembler. This will create an .EXE file. The
- command is:
-
- LINK <filename>
-
- Press Return three times in response to the prompts.
-
- 4. Use EXE2BIN.EXE to convert the program to .COM or .BIN format.
- If you are creating a .BIN file, you need only enter one parameter in
- the command line:
-
- EXE2BIN <filename>
-
- If you are creating a .COM file, you need to specify the full target
- filename:
-
- EXE2BIN <filename> <filename>.COM
-
-
- >>> Using Conditional Assembler Directives
-
- Because the differences between .COM files and .BIN files are
- minor, it is possible to generate both using the same source code. The
- following program skeleton shows how to set this up. The EQU statements
- at the top inform the assembler whether we are assembling a program for
- PC/MS-DOS or dBASE III. In the present example, we have set COM equal
- to 0 (meaning false) and D3 equal to 1 (non-zero, meaning true). We
- then use conditional directives to tell the assembler how we want the
- program created. Conditional directives are statements in your
- assembly program to direct the assembler to assemble a block of
- instructions based on a variable value. For example, IF COM (if COM is
- not zero), ORG the program at offset 100H. Then at the end of the
- program, IF COM, exit with INT 20H; otherwise, exit with a far RET.
-
-
- .LFCOND ; List false conditionals,
- PAGE 60,132 ; page length 60, line 132.
-
- COM EQU 0 ; Assemble program as .BIN
- D3 EQU 1 ; file for dBASE III.
-
- CODESEG SEGMENT BYTE PUBLIC 'CODE'
- ROUTINE PROC FAR
- ASSUME CS:CODESEG,DS:CODESEG
-
- IF COM
- ORG 100H
- ENDIF
-
- PUSH DS ; Make sure DS points to
- PUSH CS ; the current
- POP DS ; segment.
- .
- . (program goes here)
- .
- .
-
- POP DS ; Restore caller's DS.
- IF COM
- INT 20H ; INT 20H if .COM file.
- ELSE
- RET ; Far return if dBASE III
- ENDIF
-
- ROUTINE ENDP
- CODESEG ENDS
- END
-
- It is very important to load the DS register with the segment
- address contained in CS. PC/MS-DOS does this automatically for
- a .COM file, but dBASE III does not. Therefore, if your routine needs
- to access its own data, it will need to set DS correctly.
-
-
- Sample Program With Conditional Assembly
-
- Here is a program built on the skeletal structure which sets
- condensed print on an EPSON printer.
-
- ; Program ...: Printer.ASM
- ; Author ....: Ralph Davis
- ; Date ......: September 1, 1985
-
- TITLEPRINTER.ASM -- sets condensed print
-
- .LFCOND
- PAGE60,132
-
- COMEQU0
- D3EQU1
-
- CODESEGSEGMENTBYTE PUBLIC 'CODE'
- PRINTER PROCFAR
- ASSUME CS:CODESEG,DS:CODESEG
-
- IFCOM
- ORG100H
- ENDIF
-
- START:JMPSHORT ENTRY; Jump past data.
- CODESDB27,64,27,15; Printer control codes.
- CODELEN EQU$-CODES; Length of string.
- ENTRY: PUSHAX; Save registers.
- PUSHBX
- PUSHDS
- PUSHCS; Set up DS
- POPDS; with current segment.
- PUSHCX; Save CX
- PUSHDX; and DX.
- MOV BX,OFFSET CODES; Point BX to codes.
- MOVCX,CODELEN; Length of string.
- ; Controls the loop.
- GET_CODE:
- MOV DL,BYTE PTR [BX] ; Get code to send.
- MOV AH,5H ; PC/MS-DOS function 5H,
- INT 21H ; (send char to printer).
- INC BX ; Point to next code
- LOOP GET_CODE ; and print it.
-
- POPDX; Restore registers.
- POPCX
- POPDS
- POPBX
- POPAX
-
- IFCOM
- INT 20H ; INT 20H if .COM file.
- ELSE
- RET; Far return to dBASE III.
- ENDIF
-
- PRINTER ENDP
- CODESEG ENDS
- ENDSTART; End assembly.
-
-
- Assemble this program according to the instructions given earlier.
- To run it from dBASE II or dBASE III versions 1.0 and 1.1, assemble it
- as a .COM file, and enter the following commands:
-
- dBASE II:
-
- QUIT TO 'Printer'
-
- dBASE III:
-
- RUN Printer
-
- To run it from the Developer's Release of dBASE III, assemble it
- as a .BIN file, and use these commands:
-
- LOAD Printer
- CALL Printer
-